#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "oled.h"
#define SPI_CHANNEL_CNT	1
#define SPI_CHANNEL_NAME	"oled"
int oled_control_pin_number = 504; //DC  GP17     
struct spiChannelDev {
	dev_t devid;				/* 设备号 	 */
	struct cdev cdev;			/* cdev 	*/
	struct class *class;		/* 类 		*/
	struct device *device;		/* 设备 	 */
	struct device_node	*nd; 	/* 设备节点 */
	int major;					/* 主设备号 */
	void *private_data;			/* 私有数据 		*/
	int cs_gpio;				/* 片选所使用的GPIO编号		*/
};

static struct spiChannelDev spi_channel_dev;
static int oled_send_command(struct spi_device *spi_device, u8 command)
{
	int error = 0;
	u8 tx_data = command;
	struct spi_message *message;   //定义发送的消息
	struct spi_transfer *transfer; //定义传输结构体

	/*设置 D/C引脚为低电平*/
	gpio_direction_output(oled_control_pin_number, 0);

	/*申请空间*/
	message = kzalloc(sizeof(struct spi_message), GFP_KERNEL);
	transfer = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);

	/*填充message和transfer结构体*/
	transfer->tx_buf = &tx_data;
	transfer->len = 1;
	spi_message_init(message);
	spi_message_add_tail(transfer, message);

	error = spi_sync(spi_device, message);
	kfree(message);
	kfree(transfer);
	if (error != 0)
	{
		printk("spi_sync error! \n");
		return -1;
	}
	gpio_direction_output(oled_control_pin_number, 1);
	return 0;
}
static int oled_send_one_u8(struct spi_device *spi_device, u8 data)
{
	int error = 0;
	u8 tx_data = data;
	struct spi_message *message;   //定义发送的消息
	struct spi_transfer *transfer; //定义传输结构体

	/*设置 D/C引脚为高电平*/
	gpio_direction_output(oled_control_pin_number, 1);

	/*申请空间*/
	message = kzalloc(sizeof(struct spi_message), GFP_KERNEL);
	transfer = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);

	/*填充message和transfer结构体*/
	transfer->tx_buf = &tx_data;
	transfer->len = 1;
	spi_message_init(message);
	spi_message_add_tail(transfer, message);

	error = spi_sync(spi_device, message);
	kfree(message);
	kfree(transfer);
	if (error != 0)
	{
		printk("spi_sync error! \n");
		return -1;
	}
	return 0;
}
void oled_send_one_u16(struct spi_device *spi,u16 dat)
{
	oled_send_one_u8(spi,dat>>8);
	oled_send_one_u8(spi,dat);
}

void oled_address_set(struct spi_device *spi,u16 x1,u16 y1,u16 x2,u16 y2)
{
	oled_send_command(spi,0x2a);
	oled_send_one_u16(spi,x1);
	oled_send_one_u16(spi,x2);
	oled_send_command(spi,0x2b);
	oled_send_one_u16(spi,y1);
	oled_send_one_u16(spi,y2);
	oled_send_command(spi,0x2c);
}
void oled_draw_point(struct spi_device *spi,u16 x,u16 y,u16 color)
{
	oled_address_set(spi,x,y,x,y);
	oled_send_one_u16(spi,color);
} 
void oled_fill(struct spi_device *spi,u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)
{          
	u16 i,j; 
	oled_address_set(spi,xsta,ysta,xend-1,yend-1);
	for(i=ysta;i<yend;i++)
	{													   	 	
		for(j=xsta;j<xend;j++)
		{
			oled_send_one_u16(spi,color);
		}
	} 					  	    
}
void oled_draw_line(struct spi_device *spi,u16 x1,u16 y1,u16 x2,u16 y2,u16 color)
{
	u16 t; 
	int xerr=0,yerr=0,delta_x,delta_y,distance;
	int incx,incy,uRow,uCol;
	delta_x=x2-x1; //¼ÆËã×ø±êÔöÁ¿ 
	delta_y=y2-y1;
	uRow=x1;//»­ÏßÆðµã×ø±ê
	uCol=y1;
	if(delta_x>0)incx=1; //ÉèÖÃµ¥²½·½Ïò 
	else if (delta_x==0)incx=0;//´¹Ö±Ïß 
	else {incx=-1;delta_x=-delta_x;}
	if(delta_y>0)incy=1;
	else if (delta_y==0)incy=0;//Ë®Æ½Ïß 
	else {incy=-1;delta_y=-delta_y;}
	if(delta_x>delta_y)distance=delta_x; //Ñ¡È¡»ù±¾ÔöÁ¿×ø±êÖá 
	else distance=delta_y;
	for(t=0;t<distance+1;t++)
	{
		oled_draw_point(spi,uRow,uCol,color);//»­µã
		xerr+=delta_x;
		yerr+=delta_y;
		if(xerr>distance)
		{
			xerr-=distance;
			uRow+=incx;
		}
		if(yerr>distance)
		{
			yerr-=distance;
			uCol+=incy;
		}
	}
}
void oled_show_picture(struct spi_device *spi,u16 x,u16 y,u16 length,u16 width,const u8 pic[])
{
	u16 i,j;
	u32 k=0;
	oled_address_set(spi,x,y,x+length-1,y+width-1);
	for(i=0;i<length;i++)
	{
		for(j=0;j<width;j++)
		{
			oled_send_one_u8(spi,pic[k*2]);
			oled_send_one_u8(spi,pic[k*2+1]);
			k++;
		}
	}			
}
void spi_oled_init(struct spi_device *spi)
{
	oled_send_command(spi,0xEF);
	oled_send_command(spi,0xEB);
	oled_send_one_u8(spi,0x14);
	oled_send_command(spi,0xFE);
	oled_send_command(spi,0xEF);	
	oled_send_command(spi,0xEB); 
	oled_send_one_u8(spi,0x14);
	oled_send_command(spi,0x84); 
	oled_send_one_u8(spi,0x40);		
	oled_send_command(spi,0x85); 
	oled_send_one_u8(spi,0xFF);	
	oled_send_command(spi,0x86); 
	oled_send_one_u8(spi,0xFF);	
	oled_send_command(spi,0x87); 
	oled_send_one_u8(spi,0xFF);	
	oled_send_command(spi,0x88); 
	oled_send_one_u8(spi,0x0A);	
	oled_send_command(spi,0x89); 
	oled_send_one_u8(spi,0x21);	
	oled_send_command(spi,0x8A); 
	oled_send_one_u8(spi,0x00);	

	oled_send_command(spi,0x8B); 
	oled_send_one_u8(spi,0x80);	

	oled_send_command(spi,0x8C); 
	oled_send_one_u8(spi,0x01);	

	oled_send_command(spi,0x8D); 
	oled_send_one_u8(spi,0x01);	

	oled_send_command(spi,0x8E); 
	oled_send_one_u8(spi,0xFF);
 
	oled_send_command(spi,0x8F); 
	oled_send_one_u8(spi,0xFF);

	oled_send_command(spi,0xB6); 
	oled_send_one_u8(spi,0x00);
	oled_send_one_u8(spi,0x20);

	oled_send_command(spi,0x36); 
	oled_send_one_u8(spi,0x08);

	oled_send_command(spi,0x3A); 
	oled_send_one_u8(spi,0x05);

	oled_send_command(spi,0x90); 
	oled_send_one_u8(spi,0x08);
	oled_send_one_u8(spi,0x08);
	oled_send_one_u8(spi,0x08);		
	oled_send_one_u8(spi,0x08);	

oled_send_command(spi,0xBD); 
oled_send_one_u8(spi,0x06);	
	
	oled_send_command(spi,0xBC); 
oled_send_one_u8(spi,0x00);	

	oled_send_command(spi,0xFF); 
oled_send_one_u8(spi,0x60);	
oled_send_one_u8(spi,0x01);	
oled_send_one_u8(spi,0x04);	

	oled_send_command(spi,0xC3); 
oled_send_one_u8(spi,0x13);	


	oled_send_command(spi,0xC4); 
oled_send_one_u8(spi,0x13);	

	oled_send_command(spi,0xC9); 
oled_send_one_u8(spi,0x22);	

	oled_send_command(spi,0xBE); 
oled_send_one_u8(spi,0x11);	
 
	oled_send_command(spi,0xE1); 
oled_send_one_u8(spi,0x10);	
oled_send_one_u8(spi,0x0E);	

	oled_send_command(spi,0xDF); 
oled_send_one_u8(spi,0x21);	
oled_send_one_u8(spi,0x0c);	
oled_send_one_u8(spi,0x02);	

	oled_send_command(spi,0xF0); 
oled_send_one_u8(spi,0x45);	
oled_send_one_u8(spi,0x09);	
oled_send_one_u8(spi,0x08);	
oled_send_one_u8(spi,0x08);	
oled_send_one_u8(spi,0x26);	
oled_send_one_u8(spi,0x2A);	

	oled_send_command(spi,0xF1); 
oled_send_one_u8(spi,0x43);	
oled_send_one_u8(spi,0x70);	
oled_send_one_u8(spi,0x72);	
oled_send_one_u8(spi,0x36);	
oled_send_one_u8(spi,0x37);	
oled_send_one_u8(spi,0x6F);	

	oled_send_command(spi,0xF2); 
oled_send_one_u8(spi,0x45);	
oled_send_one_u8(spi,0x09);	
oled_send_one_u8(spi,0x08);	
oled_send_one_u8(spi,0x08);	
oled_send_one_u8(spi,0x26);	
oled_send_one_u8(spi,0x2A);	

	oled_send_command(spi,0xF3); 
oled_send_one_u8(spi,0x43);	
oled_send_one_u8(spi,0x70);	
oled_send_one_u8(spi,0x72);	
oled_send_one_u8(spi,0x36);	
oled_send_one_u8(spi,0x37);	
oled_send_one_u8(spi,0x6F);	

	oled_send_command(spi,0xED); 
oled_send_one_u8(spi,0x1B);	
oled_send_one_u8(spi,0x0B);

	oled_send_command(spi,0xAE); 
oled_send_one_u8(spi,0x77);	

		oled_send_command(spi,0xCD); 
oled_send_one_u8(spi,0x63);	
	

oled_send_command(spi,0x70); 
oled_send_one_u8(spi,0x07);	
oled_send_one_u8(spi,0x07);	
oled_send_one_u8(spi,0x04);	
oled_send_one_u8(spi,0x0E);	
oled_send_one_u8(spi,0x0F);	
oled_send_one_u8(spi,0x09);	
oled_send_one_u8(spi,0x07);	
oled_send_one_u8(spi,0x08);	
oled_send_one_u8(spi,0x03);	

oled_send_command(spi,0xE8); 
oled_send_one_u8(spi,0x34);	

oled_send_command(spi,0x62); 
oled_send_one_u8(spi,0x18);	
oled_send_one_u8(spi,0x0D);	
oled_send_one_u8(spi,0x71);	
oled_send_one_u8(spi,0xED);	
oled_send_one_u8(spi,0x70);	
oled_send_one_u8(spi,0x70);	
oled_send_one_u8(spi,0x18);	
oled_send_one_u8(spi,0x0F);	
oled_send_one_u8(spi,0x71);	
oled_send_one_u8(spi,0xEF);	
oled_send_one_u8(spi,0x70);	

oled_send_command(spi,0x63); 
oled_send_one_u8(spi,0x18);	
oled_send_one_u8(spi,0x11);	
oled_send_one_u8(spi,0x71);	
oled_send_one_u8(spi,0xF1);	
oled_send_one_u8(spi,0x70);	
oled_send_one_u8(spi,0x70);	
oled_send_one_u8(spi,0x18);	
oled_send_one_u8(spi,0x13);	
oled_send_one_u8(spi,0x71);	
oled_send_one_u8(spi,0xF3);	
oled_send_one_u8(spi,0x70);	

oled_send_command(spi,0x64); 
oled_send_one_u8(spi,0x28);	
oled_send_one_u8(spi,0x29);	
oled_send_one_u8(spi,0xF1);	
oled_send_one_u8(spi,0x01);	
oled_send_one_u8(spi,0xF1);	
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0x07);	

oled_send_command(spi,0x66); 
oled_send_one_u8(spi,0x3C);	
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0xCD);	
oled_send_one_u8(spi,0x67);	
oled_send_one_u8(spi,0x45);	
oled_send_one_u8(spi,0x45);	
oled_send_one_u8(spi,0x10);	
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0x00);


oled_send_command(spi,0x67); 
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0x3C);	
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0x01);	
oled_send_one_u8(spi,0x54);	
oled_send_one_u8(spi,0x10);	
oled_send_one_u8(spi,0x32);
oled_send_one_u8(spi,0x98);

oled_send_command(spi,0x74); 
oled_send_one_u8(spi,0x10);	
oled_send_one_u8(spi,0x85);	
oled_send_one_u8(spi,0x80);	
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0x00);	
oled_send_one_u8(spi,0x4E);	
oled_send_one_u8(spi,0x00);	
				
	oled_send_command(spi,0x98); 
oled_send_one_u8(spi,0x3e);	
oled_send_one_u8(spi,0x07);

	oled_send_command(spi,0x35);
	oled_send_command(spi,0x21);
	oled_send_command(spi,0x11);
	mdelay(120);
	oled_send_command(spi,0x29);
	mdelay(20);
    /*清屏*/
	oled_fill(spi,0,0,X_WIDTH,Y_WIDTH,0x0000);
	oled_draw_line(spi,20,20,200,200,0xFFFF);
}
static int spi_channel_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &spi_channel_dev;
	struct spiChannelDev *dev = (struct spiChannelDev *)filp->private_data;
	struct spi_device *spi = (struct spi_device *)(spi_channel_dev.private_data);
	spi_oled_init(spi);
	return 0;
}



static ssize_t spi_channel_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	long err = 0;
	struct spiChannelDev *dev = (struct spiChannelDev *)filp->private_data;

	int ret;
	struct spi_message m;
	struct spi_transfer *t;
	struct spi_device *spi = (struct spi_device *)dev->private_data;

	unsigned char *rx_data_buff = kzalloc(cnt, GFP_KERNEL);

	gpio_set_value(dev->cs_gpio, 0);
	t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);

	t->rx_buf = rx_data_buff;
	t->len = cnt;
	spi_message_init(&m);
	spi_message_add_tail(t, &m);
	ret = spi_sync(spi, &m);
	kfree(t);
	gpio_set_value(dev->cs_gpio, 1);

	ret = copy_to_user(buf, rx_data_buff, cnt);
	kfree(rx_data_buff);

	return cnt;
}

static int spi_channel_release(struct inode *inode, struct file *filp)
{
	return 0;
}

long spi_channel_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{
	long status = 0;
	struct spiChannelDev *dev = (struct spiChannelDev *)filp->private_data;

	switch (cmd) {
	default:
		break;
	}
	return status;
}

ssize_t spi_channel_send(struct file * filp, const char __user * buf, 
			  size_t count, loff_t *ppos)
{
	struct spiChannelDev *dev = (struct spiChannelDev *)filp->private_data;

	int ret;

	unsigned char *tx_data_buff = kzalloc(count, GFP_KERNEL);
	unsigned char *rx_data_buff = kzalloc(count, GFP_KERNEL);
	copy_from_user(tx_data_buff, buf, count);

	struct spi_message m;
	struct spi_transfer *t;
	struct spi_device *spi = (struct spi_device *)dev->private_data;

	t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);	
	gpio_set_value(dev->cs_gpio, 0);		
	t->tx_buf = tx_data_buff;
	t->rx_buf = rx_data_buff;
	t->len = count;	
	spi_message_init(&m);
	spi_message_add_tail(t, &m);
	ret = spi_sync(spi, &m);
	kfree(t);				
	gpio_set_value(dev->cs_gpio, 1);

	ret = copy_to_user(buf, rx_data_buff, count);
	kfree(tx_data_buff);
	kfree(rx_data_buff);
	return ret;
}

static const struct file_operations spi_channel_ops = {
	.owner = THIS_MODULE,
	.open = spi_channel_open,
	.read = spi_channel_read,
	.write = spi_channel_send,
	.unlocked_ioctl = spi_channel_ioctl,
	.release = spi_channel_release,
};

static int spi_channel_probe(struct spi_device *spi)
{
	int ret = 0;
	if (spi_channel_dev.major) {
		spi_channel_dev.devid = MKDEV(spi_channel_dev.major, 0);
		register_chrdev_region(spi_channel_dev.devid, SPI_CHANNEL_CNT, SPI_CHANNEL_NAME);
	} else {
		alloc_chrdev_region(&spi_channel_dev.devid, 0, SPI_CHANNEL_CNT, SPI_CHANNEL_NAME);
		spi_channel_dev.major = MAJOR(spi_channel_dev.devid);
	}
	cdev_init(&spi_channel_dev.cdev, &spi_channel_ops);
	cdev_add(&spi_channel_dev.cdev, spi_channel_dev.devid, SPI_CHANNEL_CNT);
	spi_channel_dev.class = class_create(THIS_MODULE, SPI_CHANNEL_NAME);
	if (IS_ERR(spi_channel_dev.class)) {
		return PTR_ERR(spi_channel_dev.class);
	}
	spi_channel_dev.device = device_create(spi_channel_dev.class, NULL, spi_channel_dev.devid, NULL, SPI_CHANNEL_NAME);
	if (IS_ERR(spi_channel_dev.device)) {
		return PTR_ERR(spi_channel_dev.device);
	}
	spi_channel_dev.nd = spi->dev.of_node;
	if(spi_channel_dev.nd == NULL) {
		printk("spi_channel_dev node not find!\r\n");
		return -EINVAL;
	} 
	spi->mode = SPI_MODE_3;	/*MODE3，CPOL=1，CPHA=1*/
	spi->max_speed_hz=80000000;
	spi_setup(spi);
	spi_channel_dev.private_data = spi;
	return 0;
}

static int spi_channel_remove(struct spi_device *spi)
{
	cdev_del(&spi_channel_dev.cdev);
	unregister_chrdev_region(spi_channel_dev.devid, SPI_CHANNEL_CNT);
	device_destroy(spi_channel_dev.class, spi_channel_dev.devid);
	class_destroy(spi_channel_dev.class);
	return 0;
}

static const struct spi_device_id spi_channel_id[] = {
	{"yansu,oled", 0},  
	{}
};

static const struct of_device_id spi_channel_of_match[] = {
	{ .compatible = "yansu,oled" },
	{ /* Sentinel */ }
};

static struct spi_driver spi_channel_driver = {
	.probe = spi_channel_probe,
	.remove = spi_channel_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "oled",
		   	.of_match_table = spi_channel_of_match, 
		   },
	.id_table = spi_channel_id,
};
		   
static int __init spi_channel_init(void)
{
	int ret = spi_register_driver(&spi_channel_driver);
	return ret;
}

static void __exit spi_channel_exit(void)
{
	spi_unregister_driver(&spi_channel_driver);
}

module_init(spi_channel_init);
module_exit(spi_channel_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yansu");
